package com.um.jdy.app.charge.manager;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.*;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import cn.hutool.json.JSONUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingReceiver;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingRequest;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingResult;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.um.jdy.app.charge.entity.dto.EVPayParam;
import com.um.jdy.app.charge.entity.dto.EVStartParam;
import com.um.jdy.app.charge.entity.po.EVOrderTimePrice;
import com.um.jdy.app.charge.entity.vo.EVOrderConfirmVO;
import com.um.jdy.app.charge.scheduled.EVOrderTask;
import com.um.jdy.baofu.api.BaofuPayService;
import com.um.jdy.common.activity.code.CouponsCode;
import com.um.jdy.common.activity.entity.vo.CouponsUserDetailVO;
import com.um.jdy.common.activity.entity.vo.DiscountsAndStationVO;
import com.um.jdy.common.activity.entity.vo.DiscountsVO;
import com.um.jdy.common.activity.entity.vo.StationAvailableCouponsVO;
import com.um.jdy.common.activity.enums.ActivityEnum;
import com.um.jdy.common.activity.service.CouponsUserService;
import com.um.jdy.common.activity.service.DiscountsStationService;
import com.um.jdy.common.alarm.service.AlarmRecordService;
import com.um.jdy.common.charge.entity.vo.EvOrderVO;
import com.um.jdy.common.charge.entity.vo.OrderVO;
import com.um.jdy.common.commons.manager.WxManager;
import com.um.jdy.app.device.manager.DeviceManager;
import com.um.jdy.common.base.enums.YesOrNo;
import com.um.jdy.common.charge.code.OrderCode;
import com.um.jdy.common.charge.entity.po.*;
import com.um.jdy.common.charge.enums.OrderAction;
import com.um.jdy.common.charge.enums.OrderEnum;
import com.um.jdy.common.charge.enums.OrderException;
import com.um.jdy.common.charge.enums.OrderStopReason;
import com.um.jdy.common.charge.service.*;
import com.um.jdy.common.commons.properties.HshChargingProperties;
import com.um.jdy.common.commons.service.CommissionService;
import com.um.jdy.common.device.code.DeviceCode;
import com.um.jdy.common.device.entity.po.*;
import com.um.jdy.common.device.entity.vo.FeeEbPriceVO;
import com.um.jdy.common.device.enums.DeviceEnum;
import com.um.jdy.common.device.service.GunService;
import com.um.jdy.common.device.service.PileService;
import com.um.jdy.common.device.service.StationService;
import com.um.jdy.common.finance.entity.po.PlatformBill;
import com.um.jdy.common.finance.entity.po.UserBill;
import com.um.jdy.common.finance.enums.FinancialEnum;
import com.um.jdy.common.finance.service.PlatformBillService;
import com.um.jdy.common.finance.service.UserBillService;
import com.um.jdy.common.member.code.UserCode;
import com.um.jdy.common.member.entity.po.MemberUser;
import com.um.jdy.common.member.entity.po.Wallet;
import com.um.jdy.common.member.enums.PayEnum;
import com.um.jdy.common.member.service.MemberUserService;
import com.um.jdy.common.member.service.WalletService;
import com.um.jdy.common.platform.entity.po.App;
import com.um.jdy.common.platform.entity.po.SharingSetting;
import com.um.jdy.common.platform.service.AppService;
import com.um.jdy.common.platform.service.MerchantService;
import com.um.jdy.common.platform.service.SharingSettingService;
import com.um.jdy.common.push.service.PushService;
import com.um.jdy.common.sharing.entity.po.Receiver;
import com.um.jdy.common.sharing.entity.po.Records;
import com.um.jdy.common.sharing.service.ReceiverService;
import com.um.jdy.common.sharing.service.RecordsService;
import com.um.jdy.rpc.ev.confirmation.RemoteStartTransactionConfirmation;
import com.um.jdy.rpc.ev.confirmation.RemoteStopTransactionConfirmation;
import com.um.jdy.rpc.ev.confirmation.StartChargeByRFIDConfirmation;
import com.um.jdy.rpc.ev.entity.*;
import com.um.jdy.rpc.ev.enums.ConfirmationStatus;
import com.um.jdy.rpc.factory.ServiceFactory;
import com.um.jdy.rpc.notify.RpcEVOrderNotifyService;
import com.um.springboot.starter.exception.ServiceException;
import com.um.springboot.starter.orm.manager.BaseManager;
import com.um.springboot.starter.properties.HshProperties;
import com.um.springboot.starter.utils.OrderUtil;
import com.um.springboot.starter.utils.PayUtil;
import com.um.springboot.starter.utils.StringUtil;
import com.um.springcloud.dubbo.cluster.DeviceKey;
import com.um.weixin.pay.service.WxBasePayService;
import com.um.weixin.pay.service.WxSharingService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.RoundingMode;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Title
 * @Description
 * @Author renjunwei
 * @Date 2023/5/22 10:37
 */
@Slf4j
@Service
@DubboService
public class EVOrderManager extends BaseManager<EvOrderService> implements RpcEVOrderNotifyService {

    @Autowired
    PileService pileService;

    @Autowired
    GunService gunService;

    @Autowired
    MemberUserService userService;

    @Autowired
    OrderService orderService;

    @Autowired
    StationService stationService;

    @Autowired
    WalletService walletService;

    @Autowired
    MerchantService merchantService;

    @Autowired
    DeviceManager deviceManager;

    @Autowired
    HshProperties hshProperties;

    @Autowired
    HshChargingProperties hshChargingProperties;

    @Autowired
    EVOrderManager self;

    @Autowired
    HttpServletRequest request;

    @Autowired
    TaskScheduler taskScheduler;

    @Autowired
    EvOrderService evOrderService;


    @Autowired
    UserBillService userBillService;

    @Autowired
    DataLogService dataLogService;

    @Autowired
    OrderPowerService orderPowerService;

    @Autowired
    WxSharingService wxSharingService;

    @Autowired
    WxBasePayService wxPayService;

    @Autowired
    ReceiverService receiverService;

    @Autowired
    SharingSettingService sharingSettingService;

    @Autowired
    RecordsService recordsService;

    @Autowired
    CommissionService commissionService;

    @Autowired
    OrderSharingService orderSharingService;

    @Autowired
    OrderEvTimeService orderEvTimeService;

    @Autowired
    OrderEvPriceService orderEvPriceService;

    @Autowired
    OrderPlaceService orderPlaceService;

    @Autowired
    OrderPlaceFeeService orderPlaceFeeService;

    @Autowired
    OrderPlaceTimeService orderPlaceTimeService;

    @Autowired
    OrderBillService orderBillService;

    @Autowired
    OrderFeeService orderFeeService;

    @Autowired
    WxManager wxManager;

    @Autowired
    WxBasePayService wxBasePayService;

    @Autowired
    ServiceFactory serviceFactory;

    @Autowired
    PlatformBillService platformBillService;

    @Autowired
    EvBmsInfoService bmsInfoService;

    @Autowired
    EvBmsPowerService bmsPowerService;

    @Autowired
    EVOrderTask evOrderTask;

    @Autowired
    PushService pushService;

    @Autowired
    AlarmRecordService alarmRecordService;

    @Autowired
    CouponsUserService couponsUserService;

    @Autowired
    DiscountsStationService discountsStationService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    BaofuPayService baofuPayService;

    @Autowired
    AppService appService;


    /**
     * 开启充电
     * @param param
     * @return
     */
    @Transactional
    public Map startCharge(EVStartParam param) {

        Pile pile = pileService.getEntity("code", param.getCode());
        if (pile == null) {
            throw new ServiceException(DeviceCode.NoDevice);
        }

        //查询充电网点信息
        Station station = stationService.getEntity(pile.getDeviceStationId());
        if (station == null) {
            throw new ServiceException(DeviceCode.NoStation);
        }

        if (!DeviceEnum.OpenStatus.Enable.name().equals(pile.getOpenState())) {
            throw new ServiceException(DeviceCode.DeviceNotEnabled);
        }

        if (!DeviceEnum.DeviceStatus.Online.name().equals(pile.getDeviceStatus())) {
            throw new ServiceException(DeviceCode.DeviceOffline);
        }

        Gun gun = gunService.getEntity(new String[]{"device_pile_id", "port"}, new Object[]{pile.getDevicePileId(), param.getGunNum()});
        if (gun == null) {
            throw new ServiceException(DeviceCode.NoGun);
        }

        if (DeviceEnum.PortStatus.Charging.name().equals(gun.getStatus())
                || DeviceEnum.PortStatus.Faulted.name().equals(gun.getStatus())
                || DeviceEnum.PortStatus.Offline.name().equals(gun.getStatus())
                || DeviceEnum.PortStatus.Locked.name().equals(gun.getStatus())) {
            throw new ServiceException(DeviceCode.Unavailable);
        }

        if(!DeviceEnum.PortStatus.Preparing.name().equals(gun.getStatus())){
            throw new ServiceException(DeviceCode.UninsertedGun);
        }

        MemberUser user = userService.get();
        String uid = user.getMemberUserId();

        if(getUnpaidOrders(uid).size() > 0){
            throw new ServiceException(OrderCode.HasUnpaidOrder);
        }

        //判断用户是否充电白名单 true-是 false-否
        boolean freeStatus = deviceManager.checkFreeUser(uid, pile.getDeviceStationId(), pile.getDevicePileId(),pile.getComId());

        Integer balance = walletService.getBalance(uid,pile.getComId());
        //非白名单用户检查余额是否足够
        if (!freeStatus && balance < 500) {
            throw new ServiceException(OrderCode.LowBalance);
        }

        String orderNo = OrderUtil.getOrderNo();
        String outTradeNo = OrderUtil.getWxOrderNo();

        Order order = new Order();
        order.setOrderNo(orderNo);
        order.setOutTradeNo(outTradeNo);
        order.setDeviceStationId(pile.getDeviceStationId());
        order.setDevicePileId(pile.getDevicePileId());
        order.setDeviceName(pile.getName());
        order.setDeviceImei(pile.getImei());
        order.setDeviceCode(param.getCode());
        order.setDeviceGunId(gun.getDeviceGunId());
        order.setPort(gun.getPort());
        order.setMemberUserId(uid);
        order.setComId(pile.getComId());
        order.setPlatformMerchantId(station.getPlatformMerchantId());
        order.setCreateTime(DateUtil.date());
        order.setPaySeconds(0);
        order.setPayAmount(0);//免费付款为0
        order.setPayStatus(PayEnum.Status.UnPayed.name());
        order.setStatus(OrderEnum.Status.NotStarted.name());
        order.setItemPrice("");//TODO 1元3小时
        order.setIsFree(freeStatus ? YesOrNo.Yes.name() : YesOrNo.No.name());//免费订单
        order.setDeviceType(pile.getDeviceType());
        order.setDeviceFeeId(pile.getDeviceFeeId());
        order.setAutoUseCoupons(YesOrNo.Yes.name());
        if(freeStatus || null == param.getAutoUseCoupons() || !param.getAutoUseCoupons()){
            order.setAutoUseCoupons(YesOrNo.No.name());
            if(!freeStatus && StrUtil.isNotBlank(param.getActivityCouponsId())){
                CouponsUserDetailVO userCoupons = couponsUserService.getUserCoupons(param.getActivityCouponsId(), uid);
                if(null == userCoupons){
                    throw new ServiceException(CouponsCode.CouponsNotExist);
                }
                order.setActivityCouponsUserId(userCoupons.getActivityCouponsUserId());
                couponsUserService.saveCouponsUse(userCoupons.getActivityCouponsUserId());
            }
        }

        orderService.save(order);

        EvOrder evOrder = new EvOrder();
        evOrder.setChargeOrderId(order.getChargeOrderId());
        evOrderService.save(evOrder);

        //用户如果还没有填写省市区，则自动保存
        if(StringUtil.isEmpty(user.getProvince())){
            userService.saveLocation(user,station.getProvince(),station.getCity(),station.getArea());
        }

        //记录分佣副本
        orderSharingService.addOrderSharing(order);
        //记录订单计费模式
        orderEvTimeService.addOrderEvTime(pile.getDeviceFeeId(), order.getChargeOrderId());
        orderEvPriceService.addOrderEvPrice(pile.getDeviceFeeId(), order.getChargeOrderId());
        //记录订单占位费
        orderPlaceService.addPlaceAndTime(pile.getDeviceFeeId(), order.getChargeOrderId());

        gun.setStatus(DeviceEnum.PortStatus.Charging.name());
        gunService.update(gun);

        //结束当前设备正在占位的订单
        //当用户充电完成未拔枪占位时，其他用户直接开启充电才会触发
        Order placingOrder = orderService.getEntity("order_no",gun.getOrderNo());
        if(ObjectUtil.isNotNull(placingOrder)){
            EvOrder placingEvOrder = service.getEntity(new String[]{"charge_order_id","place_status"},new Object[]{placingOrder.getChargeOrderId(),OrderEnum.PlaceStatus.Place.name()});
            if(placingEvOrder != null) {
                this.finishPlaceOrder(placingEvOrder);
            }
        }

        this.orderPaid(order.getOrderNo(), balance);

        Map returnData = new Hashtable();
        returnData.put("order_no", orderNo);
        return returnData;
    }

    /**
     * 订单支付、微信支付分授权、余额支付：后续实际开启充电
     *
     * @param orderNo 订单号
     * @param balance 充电余额
     * @throws Exception
     */
    @Lock4j(name = "processUnpaidOrder", keys = "#orderNo")
    @Transactional
    public void orderPaid(String orderNo, Integer balance) {
        // 重新查一遍订单，避免落锁前订单已被改动
        Order order = orderService.getRow("order_no", orderNo);
        if (order == null) {
            log.error("订单{}不存在！", orderNo);
            return;
        }

        if (OrderEnum.Status.Charging.name().equals(order.getStatus())) {
            log.warn("订单{}已开启充电，忽略orderPaid", orderNo);
            return;
        }

        Pile pile = pileService.getRow(order.getDevicePileId());
        if (pile == null) {
            log.error("设备" + order.getDevicePileId() + "不存在！");
            return;
        }

        EVStartChargeParams params = new EVStartChargeParams();
        params.setBalance(balance);
        params.setTransactionNo(orderNo);
        params.setCardNo(userService.get().getNo());
        params.setPhysicsNo("");
        serviceFactory.getRpcEVDeviceService(pile.getProtocol()).startCharge(new DeviceKey(order.getDeviceImei()),order.getPort(),params);
        orderService.updateRow(order);

        saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.StartChargeReq,DateUtil.date(),params,"");

        // AC创建30秒后未启动自动取消任务   DC创建60秒后未启动自动取消任务
        taskScheduler.schedule(() -> evOrderTask.timeoutNotStartedTask(orderNo),
                DateUtil.date().offset(DateField.SECOND, DeviceEnum.AcAndDc.AC.name().equals(pile.getEvType()) ? 30 : 60));

    }

    /**
     * 用户取消充电
     *
     * @param orderNo
     * @return
     */
    public void cancelCharge(String orderNo) throws Exception {
        self.stopCharge(orderNo, OrderStopReason.EB.USER_CANCEL.name(), true);
    }

    /**
     * 用户停止充电
     *
     * @param orderNo
     * @return
     */
    public void stopCharge(String orderNo) throws Exception {
        Order order = checkUserOrderExits(orderNo);

        saveChargeData(order.getComId(),order.getChargeOrderId(), OrderAction.EV.StopChargeReq, new Date(),
                MapUtil.builder().put("imei", order.getDeviceImei()).put("port", order.getPort()).build(), "用户在小程序上停止充电");

        self.stopCharge(orderNo, OrderStopReason.EB.USER_END.name(), false);
    }

    /**
     * 停止充电:内部调用
     *
     * @param orderNo
     * @param stopReason 停止原因
     * @param forceStop 是否强行结束或取消订单，不调用设备
     */
    public void stopCharge(String orderNo, String stopReason, boolean forceStop){
        // 重新查一遍订单，避免落锁前订单已被改动
        Order order = orderService.getRow("order_no", orderNo);
        if (order == null) {
            throw new ServiceException(OrderCode.NoOrder);
        }

        if (OrderEnum.Status.Completed.name().equals(order.getStatus())) {
            throw new ServiceException(OrderCode.OrderCompleted);
        }

        // 订单记录sysEndType
        order.setEndType(stopReason);
        orderService.updateRow(order);

        Pile pile = pileService.getRow(order.getDevicePileId());
        if (null == pile) { // 设备已被删除，强行结束订单
            log.warn("订单{}充电设备已被删除", order);
            saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),
                    "{}","订单充电设备已被删除");
            self.chargeFinish(orderNo,OrderEnum.Status.Completed, OrderStopReason.EV.SYS_END.name());

            return;
        }

        String deviceName = pile.getImei();
        // 设备离线
        if (!DeviceEnum.DeviceStatus.Online.name().equals(pile.getDeviceStatus())) {
            if (forceStop) {

                String tempEndType = OrderStopReason.EV.SYS_END.name();
                if(stopReason.equals(OrderStopReason.EV.OFFLINE_SYS_END.name())){
                    tempEndType =  OrderStopReason.EV.OFFLINE_SYS_END.name();
                }

                setExceptionOrder(order.getChargeOrderId(),OrderException.EV.OfflineStoped);
                saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),
                        "{}","订单充电设备不在线,订单超时或强制结算");
                self.chargeFinish(orderNo,OrderEnum.Status.Completed,tempEndType);

            } else {
                log.warn("设备{}不在线，忽略取消订单{}", deviceName, orderNo);
            }
            return;
        }

        //启动未成功
        if(OrderEnum.Status.NotStarted.name().equals(order.getStatus())){
            log.warn("订单{}启动未成功，取消订单,结束类型：{}", orderNo,stopReason);

            setExceptionOrder(order.getChargeOrderId(),OrderException.EV.StartFailed);

            saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),
                    "{}","订单启动未成功，取消订单");
            self.chargeFinish(orderNo,OrderEnum.Status.Failure,stopReason);


            return;
        }

        log.debug("stopCharge 订单停充开始");
        //设备停充
        if(forceStop || StringUtil.toInt(order.getRemoteStopCount()) >= hshChargingProperties.getRemoteStopCount()){
            log.warn("远程停止不成功，平台自己停止：{}", orderNo,stopReason);

            setExceptionOrder(order.getChargeOrderId(),OrderException.EV.StopFailed);

            saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),
                    "","远程设备停止不成功，平台自己停止及结算订单");
            self.chargeFinish(orderNo, OrderEnum.Status.Completed,stopReason);



        }else {

            //订单记录remote_stop_count为1，如果下次还要停止（设备不回复或停止不了），平台直接结束
            order.setRemoteStopCount(StringUtil.toInt(order.getRemoteStopCount()) + 1);
            orderService.updateRow(order);

            saveChargeData(order.getComId(),order.getChargeOrderId(), OrderAction.EV.StopChargeReq, new Date(),
                    MapUtil.builder().put("imei", pile.getImei()).put("port", order.getPort()).build(), null);
            serviceFactory.getRpcEVDeviceService(pile.getProtocol()).stopCharge(new DeviceKey(pile.getImei()), order.getPort());


            log.debug("stopCharge 订单停充结束");
        }
    }

    /**
     * 检查订单是否存在并且属于当前用户
     *
     * @param orderNo
     */
    public Order checkUserOrderExits(String orderNo) {
        String userId = userService.get().getMemberUserId();
        Order order = orderService.getEntity(new String[]{"order_no", "member_user_id"}, new Object[]{orderNo, userId});
        if (null == order) {
            throw new ServiceException(OrderCode.NoOrder);
        }
        return order;
    }

    /**
     * 是否分账
     * @return
     */
    public boolean isProfitSharing(String comId){
        SharingSetting sharingSetting = sharingSettingService.getRow("com_id",comId);
        if(null == sharingSetting){
            return false;
        }
        return StringUtil.equals(sharingSetting.getProfitSharing(), YesOrNo.Yes.name());
    }

    /**
     * 微信分账
     * @param order
     */
    public void wxProfitSharing(Order order){
        List<Receiver> list = receiverService.getRows("com_id",order.getComId());
        if(null == list || list.size() == 0){
            log.error("分账失败，未设置分账收款用户！");
            return;
        }

        SharingSetting sharingSetting = sharingSettingService.getRow("com_id",order.getComId());
        if(null == sharingSetting){
            log.error("分账失败，未设置分账金额比例！");
            return;
        }

        int rate = StringUtil.toInt(sharingSetting.getSharingRate());
        long amount = (order.getPayAmount() - order.getReturnAmount()) * rate / 100;
        if(amount <= 0){
            log.error("分账金额小于0，跳过分账！");
            return;
        }

        Receiver receiverMap = list.get(RandomUtil.randomInt(0, list.size()));
        String account = StringUtil.getTrim(receiverMap.getAccount());
        ProfitSharingReceiver receiver = new ProfitSharingReceiver(StringUtil.getTrim(receiverMap.getType()),
                account, amount, "分账");

        String outTradeNo = OrderUtil.getWxOrderNo();
        order.setProfitSharingOutTradeNo(outTradeNo);

        ProfitSharingRequest req = new ProfitSharingRequest();
        req.setAppid(wxManager.getPayService(order.getComId()).getConfig().getAppId());
        req.setTransactionId(StringUtil.getTrim(order.getTransactionId()));
        req.setOutOrderNo(outTradeNo);
        req.setUnfreezeUnsplit(true);
        req.setReceivers(ListUtil.toList(receiver));
        ProfitSharingResult result = null;
        try {
            result = wxSharingService.profitSharing(req);
        } catch (Exception e) {
            log.error("分账错误！", e);
            order.setProfitSharingStatus(OrderEnum.ProfitSharingStatus.FAIL.name());
            orderService.updateRow(order);
            return;
        }
        log.info("分账结果：{}", cn.hutool.json.JSONUtil.toJsonStr(result));
        // TODO 检查一下同步返回的结果是否分账已完成，如果是处理中做个定时任务查询分账结果

        order.setProfitSharingStatus(result.getState());
        orderService.updateRow(order);

        ProfitSharingResult.Receiver receiverResult = result.getReceivers().stream()
                .filter(item -> item.getAccount().equals(account)).findFirst().get();

        Records sharingRecords = new Records();
        sharingRecords.setComId(order.getComId());
        sharingRecords.setMemberUserId(receiverMap.getMemberUserId());
        sharingRecords.setChargeOrderId(order.getChargeOrderId());
        sharingRecords.setAccount(account);
        sharingRecords.setOutTradeNo(outTradeNo);
        sharingRecords.setSharingAmount((int) amount);
        sharingRecords.setSharingRate(rate);
        sharingRecords.setSharingDate(DateUtil.date());
        sharingRecords.setSharingStatus(receiverResult.getResult());
        sharingRecords.setSharingFailReason(receiverResult.getFailReason());
        recordsService.saveRow(sharingRecords);

    }

    /**
     * 获取订单充电时间（秒）
     *
     * @param order
     * @param currentTime
     * @return
     */
    public int getOrderChargeSeconds(Order order, Date currentTime) {
        int paySeconds = order.getPaySeconds();
        Date startTime = order.getCreateTime();
        int chargeSeconds = (int)Math.max(0,(currentTime.getTime() - startTime.getTime())/1000);
        return Math.min(paySeconds, chargeSeconds);
    }


    /**
     * 是否是免费充电订单
     * @param order
     * @return
     */
    public boolean isFreeOrder(Order order){
        return YesOrNo.Yes.name().equals(order.getIsFree());
    }

    /**
     * 保存记录订单操作日志
     * @param orderId 订单ID
     * @param action 操作类型
     * @param time 时间
     * @param data 其他数据
     * @param remark 描述
     */
    public void saveChargeData(String comId, String orderId, OrderAction.EV action, Date time, Object data, String remark){
        if(OrderAction.EV.TimeAndCharge == action){
            String key = redisTemplate.opsForValue().get(action.name() + orderId);
            if(null != key){
                return;
            }
            redisTemplate.opsForValue().set(action.name() + orderId, time.toString(), 30, TimeUnit.MINUTES);
        }
        DataLog dataLog = new DataLog();
        dataLog.setComId(comId);
        dataLog.setChargeOrderId(orderId);
        dataLog.setAction(action.name());
        dataLog.setActionZh(action.getValue());
        dataLog.setCreateTime(time);
        dataLog.setJsonData(JSONUtil.toJsonStr(data));
        dataLog.setRemark(remark);
        dataLogService.saveRow(dataLog);
    }


    /**
     * 记录为异常订单
     * @param orderId
     * @param exception
     */
    public void setExceptionOrder(String orderId, OrderException.EV exception){
        Order order = new Order();
        order.setChargeOrderId(orderId);
        order.setExceptionStatus(YesOrNo.No.name());
        order.setIsException(YesOrNo.Yes.name());
        order.setExceptionType(exception.name());
        orderService.updateRow(order);
    }

    /**
     * 保存充电账单记录
     * @param order
     * @param orderData
     * @return
     */
    public OrderBill saveOrderBill(Order order, EVOrderData orderData){
        OrderBill orderBill = orderBillService.getRow("charge_order_id",order.getChargeOrderId());
        if(ObjectUtil.isNotNull(orderBill)){
            return orderBill;
        }

        orderBill = new OrderBill();
        BeanUtil.copyProperties(orderData, orderBill);
        orderBill.setChargeOrderId(order.getChargeOrderId());
        orderBill.setComId(order.getComId());
        orderBill.setChargeStartTime(orderData.getBeginTime());
        orderBill.setChargeEndTime(orderData.getEndTime());

        orderBillService.saveRow(orderBill);
        return orderBill;
    }

    /**
     * 保存订单费用
     * @param order
     */
    public OrderFee saveOrderFee(Order order){
        DateTime now = DateTime.now();
        String value = DateUtil.format(now,"HH:mm");

        //根据当前时间返回对应时段价格表
        EVOrderTimePrice priceItem = service.getViewModel("get_evorder_current_price",EVOrderTimePrice.class,
                new Object[]{order.getComId(),order.getChargeOrderId(),value,value});
        if (priceItem == null) {
            log.error("priceItem is {}",priceItem);
            return null;
        }

        //根据开始时间和结束时间查询当前时段的子订单
        String time = DateUtil.format(now, DatePattern.NORM_DATETIME_PATTERN);
        OrderFee orderFee = orderFeeService.getEntityBySqlName("get_order_fee_by_range",order.getComId(),order.getChargeOrderId(),time,time);

        //创建此时段订单费用
        if (orderFee == null) {
            return this.addOrderFee(order, priceItem, now);
        }

        //更新此时段订单费用
        return this.updateOrderFee(orderFee, priceItem, now, order);
    }

    /**
     * 新增订单费用
     * @param order
     * @param itemPrice
     * @param currentTime
     * @return
     */
    public OrderFee addOrderFee(Order order, EVOrderTimePrice itemPrice, Date currentTime) {
        List<OrderFee> orderFeeList = orderFeeService.getRows("charge_order_id",order.getChargeOrderId());

        EvOrder evOrder = service.getRow("charge_order_id",order.getChargeOrderId());

        OrderFee orderFee = new OrderFee();
        orderFee.setChargeOrderId(order.getChargeOrderId());
        orderFee.setComId(order.getComId());
        orderFee.setCreateTime(currentTime);
        orderFee.setChargeStartTime(currentTime);
        orderFee.setChargeEndTime(currentTime);
        orderFee.setChargePeriod(itemPrice.getType());
        orderFee.setStartTime(itemPrice.getStartTime());
        orderFee.setEndTime(itemPrice.getEndTime());
        orderFee.setUnitPrice(itemPrice.getUnitPrice() / 100);
        orderFee.setServiceFee(itemPrice.getServiceFee() / 100);
        orderFee.setStatus(FinancialEnum.BillStatus.Effective.name());
        orderFee.setChargeCapacity(evOrder.getChargeCapacity() - orderFeeList.stream().mapToInt(item -> item.getChargeCapacity()).sum());
        orderFee.setTotalAmount(order.getChargeAmount() - orderFeeList.stream().mapToInt(item -> item.getTotalAmount()).sum());

        orderFeeService.saveRow(orderFee);
        return orderFee;
    }

    /**
     * 更新订单费用
     * @param orderFee
     * @param itemPrice
     * @param currentTime
     * @param order
     */
    public OrderFee updateOrderFee(OrderFee orderFee, EVOrderTimePrice itemPrice, Date currentTime, Order order) {
        List<OrderFee> orderFeeList = orderFeeService.getListBySqlName("get_order_fee_list",
                new Object[]{order.getComId(),order.getChargeOrderId(),orderFee.getChargeOrderFeeId()});

        EvOrder evOrder = service.getRow("charge_order_id",order.getChargeOrderId());
        long chargeTimeMin = DateUtil.between(orderFee.getChargeStartTime(), currentTime, DateUnit.MINUTE);
        long chargeTimeSec = DateUtil.between(orderFee.getChargeStartTime(), currentTime, DateUnit.SECOND);

        //计算本次充电通知使用度数
        int currentChargeCapacity = evOrder.getChargeCapacity() - orderFeeList.stream().mapToInt(item -> item.getChargeCapacity()).sum();
        Double powerConsumption = currentChargeCapacity / 1000.0;

        int chargeAmount = NumberUtil.round(NumberUtil.mul(powerConsumption, itemPrice.getUnitPrice(), 0.01), 0, RoundingMode.UP).intValue();
        int serviceAmount = NumberUtil.round(NumberUtil.mul(powerConsumption, itemPrice.getServiceFee(), 0.01), 0, RoundingMode.UP).intValue();

        orderFee.setChargeAmount(chargeAmount);
        orderFee.setServiceAmount(serviceAmount);
        orderFee.setChargeCapacity(currentChargeCapacity);
        orderFee.setTotalAmount(order.getChargeAmount() - orderFeeList.stream().mapToInt(item -> item.getTotalAmount()).sum());
        orderFee.setChargeSeconds((int)chargeTimeSec);
        orderFee.setChargeMinutes((int)chargeTimeMin);
        orderFee.setChargeEndTime(currentTime);
        orderFeeService.updateRow(orderFee);

        return orderFee;
    }

    /**
     *
     * 结束占位，计算占位费
     * @param evOrder 订单对象
     */
    public void finishPlaceOrder(EvOrder evOrder){

        OrderPlace orderPlace = orderPlaceService.getRow("charge_order_id",evOrder.getChargeOrderId());
        if(orderPlace == null)
            return;

        saveChargeData(orderPlace.getComId(),orderPlace.getChargeOrderId(),OrderAction.EV.StopPlace,new Date(),null,"");

        if(orderPlace.getStatus().equals(YesOrNo.Yes.name())) {
            evOrder.setFreeDuration(orderPlace.getFreeDuration());
            evOrder.setPlaceStatus(OrderEnum.PlaceStatus.Completed.name());
            evOrder.setPlaceEndTime(DateUtil.date());
            long between = DateUtil.between(evOrder.getPlaceBeginTime(), evOrder.getPlaceEndTime(), DateUnit.SECOND);
            evOrder.setPlaceSeconds((int) Math.max(0, between - orderPlace.getFreeDuration() * 60));

            if (orderPlace.getStatus().equals(YesOrNo.Yes.name()) && evOrder.getPlaceSeconds() > 0) {//收占位费
                evOrder.setPlaceAmount(evOrderService.savePlaceFee(orderPlace, evOrder, true));
            } else {
                evOrder.setPlaceAmount(0);
            }
        }
        service.updateRow(evOrder);

        Order order = orderService.getRow("charge_order_id",evOrder.getChargeOrderId());
        if(order != null) {
            Order updateOrder = new Order();
            updateOrder.setChargeOrderId(order.getChargeOrderId());
            updateOrder.setOrderAmount(evOrder.getReportAmount() + evOrder.getPlaceAmount());
            if(YesOrNo.Yes.name().equals(order.getIsFree())){
                updateOrder.setFreeAmount(order.getOrderAmount());
                updateOrder.setChargeAmount(0);
                updateOrder.setOrderAmount(0);
            }
            orderService.updateRow(updateOrder);
            self.chargeFinish(order.getOrderNo(),null,null);
        }
    }



    /**
     * 统一处理订单结束方式
     *
     * @param orderNo        订单号
     * @param stopReason        设备结束类型
     */
    @Lock4j(keys = "#orderNo")
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void chargeFinish(String orderNo,OrderEnum.Status status, String stopReason) {
        //更新订单状态
        Order order = orderService.getRow("order_no", orderNo);
        if (order == null) {
            return;
        }

        if(status != null) {
            order.setStatus(status.name()); //充电完成
        }

        if(stopReason != null && StringUtil.isEmpty(order.getEndType())){
            order.setEndType(stopReason);
        }


        //更新结束时间
        order.setEndTime(DateUtil.date());

        Gun gun = gunService.getRow(order.getDeviceGunId());
        if (gun != null) {
            // 订单号一致则将充电口状态改回空闲
            if (orderNo.equals(gun.getOrderNo())) {
                gun.setOrderNo("");
                gun.setStatus(DeviceEnum.PortStatus.Available.name());
                gunService.updateRow(gun);
            }
        } else {
            log.warn("订单{}充电充电口不存在，忽略更新充电口状态", orderNo);
        }

        //免费充电
        if (isFreeOrder(order)) {
            order.setFreeAmount(order.getOrderAmount());//增加免费金额字段来记录充电费用
            order.setChargeAmount(0);//充电金额为0，才不影响其他报表统计
            order.setOrderAmount(0);
        }
        orderService.updateRow(order);

        walletPayOrder(orderNo);
    }

    /**
     * 钱包自动支付订单
     * @param orderNo
     */
    public void walletPayOrder(String orderNo){

        //更新订单状态
        Order order = orderService.getRow("order_no", orderNo);
        if (order == null) {
            return;
        }

        if(order.getPayStatus().equals(PayEnum.Status.Payed.name())){
            log.info("订单已支付,订单号：{}",orderNo);
            return;
        }

        Pile pile = pileService.getRow("device_pile_id", order.getDevicePileId());
        if (pile == null) {
            log.info("订单设备不存在,设备ID：{}",order.getDevicePileId());
            return;
        }

        EvOrder evOrder = evOrderService.getRow("charge_order_id", order.getChargeOrderId());

        String uid = order.getMemberUserId();
        //判断用户是否充电白名单 true-是 false-否
        boolean freeStatus = isFreeOrder(order);

        saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),"","订单开始结算");

        Date payTime = DateUtil.date();
        if(!freeStatus) {
            int payAmount = order.getOrderAmount();
            if(payAmount > 0) {

                orderService.updateRow(order);
                evOrderService.updateRow(evOrder);
                EVOrderConfirmVO confirm = confirm(order.getChargeOrderId());

                if(null != confirm.getSelectedCoupons()){
                    order.setActivityCouponsUserId(confirm.getSelectedCoupons().getActivityCouponsUserId());
                    order.setCouponsAmount(confirm.getSelectedCoupons().getMoney());
                    order.setCouponsRemark(confirm.getSelectedCoupons().getTitle());
                }else if(StrUtil.isNotBlank(order.getActivityCouponsUserId())){
                    couponsUserService.returnCouponsUse(order.getActivityCouponsUserId());
                }
                if(null != confirm.getDiscounts()){
                    order.setDiscountsAmount(confirm.getDiscounts().getPrice());
                    order.setDiscountsType(confirm.getDiscounts().getType());
                    order.setDiscountsRemark(confirm.getDiscounts().getTitle());
                }
                payAmount = confirm.getPayAmount();

                if(walletService.getBalance(uid,order.getComId()) >= payAmount) {
                    //记录钱包流水，金额为付款金额
                    Wallet memberWallet = new Wallet();
                    memberWallet.setComId(order.getComId());
                    memberWallet.setMemberUserId(uid);
                    memberWallet.setOrderNo(orderNo);
                    memberWallet.setNo(OrderUtil.getOrderNo());
                    memberWallet.setType(FinancialEnum.IncomeOrExpend.Expend.name());
                    memberWallet.setAmount(payAmount);
                    memberWallet.setStatus(FinancialEnum.BillStatus.Effective.name());
                    memberWallet.setEvent(FinancialEnum.WalletBillEvent.Charge.name());
                    memberWallet.setCreateTime(payTime);
                    walletService.saveRow(memberWallet);

                    order.setPayStatus(PayEnum.Status.Payed.name());
                    order.setPayTime(payTime);
                    order.setPayAmount(payAmount);
                    order.setPayWay(PayEnum.PayWay.Wallet.name());

                    saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),"","订单已支付，开始分账");
                    //分佣计算-邓南辉新版计算方式
                    int profit = commissionService.calcCommission(order.getChargeOrderId(), order.getCreateTime(), orderNo, order.getOutTradeNo(), order.getPayAmount());
                    order.setProfit(profit);
                    saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),"","分账已完成");

                }else {
                    //throw new ServiceException(UserCode.NoBalance);
                    log.info("用户余额不足,订单待支付：{}",order.getOrderNo());
                    saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),"","用户余额不足，等待用户支付");
                }
            }else{

                // 付款金额为0, 退回优惠券
                if(StrUtil.isNotBlank(order.getActivityCouponsUserId())){
                    couponsUserService.returnCouponsUse(order.getActivityCouponsUserId());
                }

                order.setPayStatus(PayEnum.Status.Payed.name());
                order.setPayTime(payTime);
                saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),"","订单金额为0,已结算");
            }
        }else{
            order.setPayStatus(PayEnum.Status.Payed.name());
            order.setPayTime(payTime);
            saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),"","免费订单无需结算");
        }

        orderService.updateRow(order);
        evOrderService.updateRow(evOrder);

    }

    public EVOrderConfirmVO confirm(String chargeOrderId){
        //更新订单状态
        Order order = orderService.getRow(chargeOrderId);
        if (order == null) {
            throw new ServiceException(OrderCode.NoOrder);
        }

        if(order.getPayStatus().equals(PayEnum.Status.Payed.name())){
            throw new ServiceException(OrderCode.Paid);
        }

        EvOrder evOrder = evOrderService.getRow("charge_order_id", order.getChargeOrderId());
        //非异常订单才判断是否占位
        if(StringUtil.equals(order.getIsException(),YesOrNo.No.name()) && OrderEnum.PlaceStatus.Place.name().equals(evOrder.getPlaceStatus())){
            throw new ServiceException(OrderCode.PlacingCannotPaid);
        }

        int payAmount = order.getOrderAmount();

        CouponsUserDetailVO selectedCoupons = null;

        // 自动使用优惠券
        if(YesOrNo.Yes.name().equals(order.getAutoUseCoupons())){
            List<StationAvailableCouponsVO> couponsList = couponsUserService.getStationAvailableList(order.getDeviceStationId(),
                    DeviceEnum.Type.EV.name(), order.getMemberUserId());
            StationAvailableCouponsVO stationAvailableCouponsVO = couponsList.stream().filter(item -> item.getLimitMoney() <= order.getOrderAmount()).findFirst().orElse(null);
            selectedCoupons = BeanUtil.toBean(stationAvailableCouponsVO, CouponsUserDetailVO.class);
            if(null != selectedCoupons){
                payAmount -= selectedCoupons.getMoney();
                couponsUserService.saveCouponsUse(selectedCoupons.getActivityCouponsUserId());
            }

            // 已选中优惠券
        }else if(StrUtil.isNotBlank(order.getActivityCouponsUserId())){
            selectedCoupons = couponsUserService.getUserCoupons(order.getActivityCouponsUserId());
            if(selectedCoupons.getLimitMoney() <= order.getOrderAmount()){
                payAmount -= selectedCoupons.getMoney();
            }else{
                selectedCoupons = null;
            }
        }

        DiscountsVO selectedDiscount = null;
        if(null == selectedCoupons || YesOrNo.Yes.name().equals(selectedCoupons.getIsActivityMerge())){
            List<DiscountsVO> discountsList = discountsStationService.getDiscountsList(order.getDeviceStationId(),
                    DeviceEnum.Type.EB.name());
            selectedDiscount = discountsList.stream().filter(item ->
                    item.getFullPrice() <= order.getOrderAmount()).findFirst().orElse(null);
            if(null != selectedDiscount){
                payAmount -= ActivityEnum.DiscountsType.Money.name().equals(selectedDiscount.getType()) ?
                        selectedDiscount.getPrice() : payAmount * selectedDiscount.getScale() / 100;
            }
        }

        if(payAmount < 0){
            payAmount = 0;
        }

        EVOrderConfirmVO confirm = new EVOrderConfirmVO();
        confirm.setOrder(BeanUtil.toBean(order, OrderVO.class));
        confirm.setEvOrder(BeanUtil.toBean(evOrder, EvOrderVO.class));
        confirm.setSelectedCoupons(selectedCoupons);
        confirm.setDiscounts(selectedDiscount);
        confirm.setPayAmount(payAmount);
        return confirm;

    }

    /**
     * 支付
     * @param param
     */
    public Map pay(EVPayParam param){
        EVOrderConfirmVO confirm = confirm(param.getChargeOrderId());

        Order order = BeanUtil.toBean(confirm.getOrder(), Order.class);
        int payAmount = confirm.getPayAmount();

        MemberUser user = userService.getEntity(order.getMemberUserId());
        if(null == user){
            throw new ServiceException(UserCode.NoUser);
        }

        if(null != confirm.getSelectedCoupons()){
            order.setActivityCouponsUserId(confirm.getSelectedCoupons().getActivityCouponsUserId());
            order.setCouponsAmount(confirm.getSelectedCoupons().getMoney());
            order.setCouponsRemark(confirm.getSelectedCoupons().getTitle());
        }else if(StrUtil.isNotBlank(order.getActivityCouponsUserId())){
            couponsUserService.returnCouponsUse(order.getActivityCouponsUserId());
        }
        if(null != confirm.getDiscounts()){
            order.setDiscountsAmount(confirm.getDiscounts().getPrice());
            order.setDiscountsType(confirm.getDiscounts().getType());
            order.setDiscountsRemark(confirm.getDiscounts().getTitle());
        }
        order.setPayAmount(payAmount);

        Map returnData = MapUtil.newHashMap();

        switch (param.getPayWay()){
            case Wallet:
                if(walletService.getBalance(order.getMemberUserId(),order.getComId()) < payAmount) {
                    throw new ServiceException(UserCode.NoBalance);
                }

                order.setPayStatus(PayEnum.Status.Payed.name());
                order.setPayTime(DateUtil.date());
                order.setPayWay(PayEnum.PayWay.Wallet.name());
                orderService.updateEntity(order);

                //记录钱包流水，金额为付款金额
                Wallet memberWallet = new Wallet();
                memberWallet.setMemberUserId(order.getMemberUserId());
                memberWallet.setOrderNo(order.getOrderNo());
                memberWallet.setNo(OrderUtil.getOrderNo());
                memberWallet.setType(FinancialEnum.IncomeOrExpend.Expend.name());
                memberWallet.setAmount(payAmount);
                memberWallet.setStatus(FinancialEnum.BillStatus.Effective.name());
                memberWallet.setEvent(FinancialEnum.WalletBillEvent.Charge.name());
                memberWallet.setCreateTime(DateUtil.date());
                walletService.save(memberWallet);

                saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),"","订单已支付，开始分账");
                //分佣计算-邓南辉新版计算方式
                int profit = commissionService.calcCommission(order.getChargeOrderId(), order.getCreateTime(), order.getOrderNo(), order.getOutTradeNo(), order.getPayAmount());
                order.setProfit(profit);
                orderService.updateEntity(order);

                saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),"","分账已完成");

                break;

            case Wxpay:
                /** todo 改成宝付支付
                String notifyUrl = StrUtil.builder(hshProperties.getConfig().getDomain(),
                        "/api/v1/order/charge/ev/wxpay/notify/", userService.getComId()).toString();
                WxPayUnifiedOrderV3Result.JsapiResult jsapiResult;
                try {
                    wxManager.getPayService(userService.getComId());
                    jsapiResult = wxBasePayService.createOrderByJsapi(order.getOutTradeNo(), user.getMiniOpenId(),
                            PayUtil.toTotalFee(order.getPayAmount()), PayEnum.Title.Charge.getValue(), notifyUrl);
                } catch (WxPayException e) {
                    log.error("微信支付创建订单失败", e);
                    throw new ServiceException(e.getCustomErrorMsg());
                }
                 returnData = BeanUtil.beanToMap(jsapiResult, true, true);
                */
                String type = "chargeEv";
                App app = appService.getEntity("com_id", userService.getComId());
                returnData = baofuPayService.createOrderByJsapi(order.getOutTradeNo(),PayUtil.toTotalFee(order.getPayAmount()),PayEnum.Title.Charge.getValue(),user.getMiniOpenId(),app.getMiniAppId(),app.getWxMchId(),type,userService.getComId());

        }

        return returnData;

    }

    /**
     * 微信支付回调
     *
     * @param request
     */
    public void wxPayNotify(HttpServletRequest request, String comId) {

        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = null;
        try {
            wxManager.getPayService(comId);
            wxPayOrderNotifyV3Result = wxBasePayService.parseOrderNotify(request);
        } catch (WxPayException e) {
            log.error("解析微信支付通知失败", e);
        }

        String outTradeNo = wxPayOrderNotifyV3Result.getResult().getOutTradeNo();
        DateTime payTime = DateUtil.parse(wxPayOrderNotifyV3Result.getResult().getSuccessTime());
        String transactionId = wxPayOrderNotifyV3Result.getResult().getTransactionId();
        this.payFinish(outTradeNo, transactionId, payTime, PayEnum.PayWay.Wxpay);
    }

    /**
     * 支付宝支付回调
     * @param request
     * @throws Exception
     */
    public void aliPayNotify(HttpServletRequest request) {

//        Map<String, String> parameters = ServletUtil.getParamMap(request);
//        log.info("支付宝充电支付回调异步通知：{}", cn.hutool.json.JSONUtil.toJsonStr(parameters));
//
//        boolean verifyResult = false;
//        try {
//            verifyResult = Factory.Payment.Common().verifyNotify(parameters);
//        } catch (Exception e) {
//            throw new RuntimeException(e);
//        }
//
//        if (!verifyResult || !StrUtil.equals("TRADE_SUCCESS", parameters.get("trade_status"))) {
//            log.error("支付宝支付异步通知验签未通过！");
//        }
//
//        //处理订单支付后续
//        String outTradeNo = parameters.get("out_trade_no");
//        String payTime = parameters.get("gmt_payment");
//
//        Order order = orderService.getRow("out_trade_no", outTradeNo);
//        if (null == order) {
//            log.error("支付宝订单{}不存在！", outTradeNo);
//            return;
//        }
//
//        self.orderPaid(order.getOrderNo(), DateUtil.parse(payTime));
    }

    /**
     * 支付完成
     * @param outTradeNo
     * @param transactionId
     * @param payTime
     */
    public void payFinish(String outTradeNo, String transactionId, Date payTime, PayEnum.PayWay payWay){
        Order order = orderService.getRow("out_trade_no", outTradeNo);
        if (order == null) {
            log.error("订单交易号[{}]不存在", outTradeNo);
            return;
        }

        if(null == order){
            log.error("订单[{}]不存在", order.getOrderNo());
            return;
        }

        if(order.getPayStatus().equals(PayEnum.Status.Payed.name())){
            log.error("订单[{}]已支付", order.getOrderNo());
            return;
        }

        order.setPayAmount(order.getOrderAmount());
        order.setPayStatus(PayEnum.Status.Payed.name());
        order.setPayTime(payTime);
        order.setPayWay(payWay.name());
        order.setTransactionId(transactionId);
        orderService.updateRow(order);

        //插入用户账单流水：退回余额
        UserBill userBill = new UserBill();
        userBill.setComId(order.getComId());
        userBill.setMemberUserId(order.getMemberUserId());
        userBill.setNo(OrderUtil.getOrderNo());
        userBill.setOutTradeNo(order.getOutTradeNo());
        userBill.setOrderNo(order.getOrderNo());
        userBill.setAmount(order.getPayAmount());
        userBill.setType(FinancialEnum.IncomeOrExpend.Expend.name());//收入支出类型：0-收入，1-支出
        userBill.setEvent(FinancialEnum.UserBillEvent.Charge.name());
        userBill.setStatus(FinancialEnum.BillStatus.Effective.name());//账单状态：0未生效|1已生效|2已失效
        userBill.setCreateTime(DateUtil.date());
        userBillService.saveRow(userBill);

        //插入平台账单流水：充电退回
        PlatformBill platformBill = new PlatformBill();
        platformBill.setComId(order.getComId());
        platformBill.setNo(OrderUtil.getOrderNo());
        platformBill.setOutTradeNo(outTradeNo);
        platformBill.setOrderNo(order.getOrderNo());
        platformBill.setAmount(order.getPayAmount());
        platformBill.setType(FinancialEnum.IncomeOrExpend.Income.name());//收入支出类型：0-收入，1-支出
        platformBill.setEvent(FinancialEnum.PlatformBillEvent.Charge.name());
        platformBill.setStatus(FinancialEnum.BillStatus.Effective.name());//账单状态：0未生效|1已生效|2已失效
        platformBill.setCreateTime(DateUtil.date());
        platformBillService.saveRow(platformBill);

        saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),"","订单已支付，开始分账");
        //分佣计算-邓南辉新版计算方式
        int profit = commissionService.calcCommission(order.getChargeOrderId(), order.getCreateTime(), order.getOrderNo(), order.getOutTradeNo(), order.getPayAmount());
        order.setProfit(profit);
        orderService.updateRow(order);
        saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderFinish,new Date(),"","分账已完成");
    }

    /**
     * 查询未付款订单
     * @return
     */
    public List<Order> getUnpaidOrders(String uid){
        List<Order> list = orderService.getList(new String[]{"status", "pay_status", "member_user_id"},
                new String[]{OrderEnum.Status.Completed.name(), PayEnum.Status.UnPayed.name(), uid},
                new String[]{"create_time"}, new String[]{"desc"});
        return list;
    }

    /*************************************************************************以下是RPC 回调的业务方法****************************************/

    //TODO 暂不实现，v3分支实现
    @Override
    public StartChargeByRFIDConfirmation reqStartCharge(String seq, String imei, EVStartChargeData startChargeData) {
        return new StartChargeByRFIDConfirmation();
    }

    /**
     * 桩回复开启充电
     * @param seq
     * @param imei IMEI码
     * @param transactionNo 订单号
     * @param port 枪号
     * @param confirmation 回复数据
     */
    @Override
    public void respStartCharged(String seq, String imei, String transactionNo, Integer port, RemoteStartTransactionConfirmation confirmation){
        log.info("汽车充电桩[{}-{}]订单[{}]回复开启充电：{}", imei, port, transactionNo, JSONUtil.toJsonStr(confirmation));

        Order order = orderService.getRow("order_no",transactionNo);
        if(order == null)
            return;

        saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.StartChargeResp,new Date(),confirmation,"");

        Gun gun = gunService.getRow(order.getDeviceGunId());

        if(confirmation != null && confirmation.getStatus() == ConfirmationStatus.Accepted){
            order.setStatus(OrderEnum.Status.Charging.name());
            order.setStartTime(DateUtil.date());

            gun.setOrderNo(order.getOrderNo());

            try {
                this.saveOrderFee(order);
            } catch (Exception e) {
                log.error("更新订单费用失败 >>> \n 订单号：{} \n 错误原因：", order.getOrderNo(), e);
            }
        }else{
            order.setStatus(OrderEnum.Status.Failure.name());
            setExceptionOrder(order.getChargeOrderId(), OrderException.EV.StartFailed);

            gun.setStatus(DeviceEnum.PortStatus.Available.name());
        }

        gunService.updateRow(gun);
        orderService.updateRow(order);
    }

    /**
     * 桩回复停止充电
     * @param seq
     * @param imei IMEI码
     * @param port 枪号
     * @param confirmation 回复数据
     */
    @Override
    public void respStopCharged(String seq, String imei, Integer port, RemoteStopTransactionConfirmation confirmation){
        log.info("汽车充电桩[{}-{}]回复停止充电：{}", imei, port, JSONUtil.toJsonStr(confirmation));

        Pile pile = pileService.getRow("imei",imei);
        if(pile == null){
            return;
        }

        Order order = orderService.getRow(new String[]{"device_code","port","status"},new Object[]{pile.getCode(),port,OrderEnum.Status.Charging.name()});
        if(order == null)
            return;

        saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.StopChargeResp,new Date(),confirmation,"");

        if(confirmation.getStatus().name().equals(ConfirmationStatus.Accepted.name())){
            //不在此修改订单状态，改为由设备上报结束再修改状态
            //order.setStatus(OrderEnum.Status.Completed.name());
        }else{
            log.error("订单停止失败：{}，原因：{}",order.getOrderNo(),confirmation.getReason());
        }

        //orderService.updateRow(order);
    }

    /**
     * 请求确认订单数据
     *
     * @param seq        序列号
     * @param imei       IMEI码
     * @param orderData  订单数据
     */
    @Override
    public boolean reqConfirmOrder(String seq, String imei, EVOrderData orderData){
        log.info("汽车充电桩[{}]请求确认订单：{}", imei, JSONUtil.toJsonStr(orderData));
        return self.selfReqConfirmOrder(seq, imei, orderData);
    }

    /**
     * 请求确认订单数据
     *
     * @param seq        序列号
     * @param imei       IMEI码
     * @param orderData  订单数据
     */
    @Lock4j(name = "orderNotify", keys = {"#imei"})
    public boolean selfReqConfirmOrder(String seq, String imei, EVOrderData orderData){
        log.info("汽车充电桩[{}]请求确认订单：{}", imei, JSONUtil.toJsonStr(orderData));

        Order order = orderService.getRow("order_no",orderData.getTransactionNo());
        if(order == null){
            return false;
        }

        String remark = "设备上报订单进行确认";
        if(orderData.getEndType() != null)
            remark +=",结束原因："+orderData.getEndType().getKey();
        saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderConfirm,new Date(),orderData,remark);

        EvOrder evOrder = evOrderService.getRow("charge_order_id",order.getChargeOrderId());
        Gun gun = gunService.getRow(order.getDeviceGunId());
        OrderPlace orderPlace = orderPlaceService.getRow("charge_order_id",order.getChargeOrderId());

        if(null == evOrder || null == gun || null == orderPlace){
            return false;
        }

        //充电结束，判断枪是否归位，如未归位且未开始算占位费，开始计算占位费
        // TODO 这里是否归还改为是否插枪，部分设备无法获取枪归位状态
        if(gun.getIsInserted().equals(YesOrNo.Yes.name()) &&
                !evOrder.getPlaceStatus().equals(OrderEnum.PlaceStatus.Place.name()) &&
                orderPlace.getStatus().equals(YesOrNo.Yes.name())){
            evOrder.setPlaceStatus(OrderEnum.PlaceStatus.Place.name());
            evOrder.setPlaceBeginTime(new Date());

            saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.StartPlace,new Date(),orderData,"");
        }

        List<OrderFee> orderFeeList = orderFeeService.getRows("charge_order_id", order.getChargeOrderId());

        evOrder.setServiceAmount(orderFeeList.stream().mapToInt(item -> item.getServiceAmount()).sum());

        //充电已完成
        order.setStatus(OrderEnum.Status.Completed.name());
        order.setChargeAmount(orderFeeList.stream().mapToInt(item -> item.getChargeAmount()).sum());
        order.setEnergyFee(order.getChargeAmount());
        order.setServiceFee(evOrder.getServiceAmount());
        order.setOrderAmount(orderData.getTotalAmount());
        if(YesOrNo.Yes.name().equals(order.getIsFree())){
            order.setFreeAmount(order.getOrderAmount());
            order.setChargeAmount(0);
            order.setOrderAmount(0);
        }

        if(null == order.getStartTime()){
            // TODO 这里要检查一下， 使用设备上报的时间是否跟我们的创建时间一致
            order.setStartTime(orderData.getBeginTime());
        }
        //修改为系统时间，以免桩端上报时间不准确 orderData.getEndTime() == null ? new Date() : orderData.getEndTime()
        order.setEndTime(DateUtil.date());
        if(StrUtil.isEmpty(order.getEndType())) {
            order.setEndType(orderData.getEndType().name());
        }

        order.setChargeCapacity(orderData.getTotalCapacity());
        order.setDuration((int) DateUtil.between(order.getEndTime(), order.getStartTime(), DateUnit.SECOND));
        orderService.updateRow(order);

        evOrder.setReportAmount(orderData.getTotalAmount());
        evOrder.setBeginMeterValue(orderData.getStartMeterValue());
        evOrder.setEndMeterValue(orderData.getEndMeterValue());
        evOrder.setChargeCapacity(orderData.getTotalCapacity());
        evOrderService.updateRow(evOrder);

        //保存充电账单
        this.saveOrderBill(order, orderData);

        //不收占位费，则自动结束
        if(orderPlace.getStatus().equals(YesOrNo.No.name()) || gun.getIsInserted().equals(YesOrNo.No.name())) {
            self.chargeFinish(order.getOrderNo(), OrderEnum.Status.Completed, orderData.getEndType().name());
        }

        //推送
        pushService.pushChargeEnd(order);

        return true;
    }

    /**
     * 充电结束上报
     *
     * @param seq        序列号
     * @param imei       IMEI码
     * @param chargingEndData  充电结束数据
     */
    @Override
    public void reqChargingEnd(String seq, String imei, EVChargingEndData chargingEndData){
        log.info("汽车充电桩[{}]充电结束上报：{}", imei, JSONUtil.toJsonStr(chargingEndData));
        self.selfReqChargingEnd(seq, imei, chargingEndData);
    }

    /**
     * 充电结束上报
     *
     * @param seq        序列号
     * @param imei       IMEI码
     * @param chargingEndData  充电结束数据
     */
    @Lock4j(name = "orderNotify", keys = {"#imei"})
    public void selfReqChargingEnd(String seq, String imei, EVChargingEndData chargingEndData){
        log.info("汽车充电桩[{}]充电结束上报：{}", imei, JSONUtil.toJsonStr(chargingEndData));

        //充电结束，判断枪是否归位，如没有，开始计算占位费
        Order order = orderService.getRow("order_no",chargingEndData.getTransactionNo());
        if(order != null){

            saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.OrderStop,new Date(),chargingEndData,"设备上报订单结束");

            Gun gun = gunService.getRow("device_gun_id",order.getDeviceGunId());

            //插枪状态，开始计算占位费
            if(gun.getIsInserted().equals(YesOrNo.Yes.name())){

                EvOrder evOrder = evOrderService.getRow("charge_order_id",order.getChargeOrderId());
                OrderPlace orderPlace = orderPlaceService.getRow("charge_order_id",order.getChargeOrderId());
                if(evOrder != null && orderPlace.getStatus().equals(YesOrNo.Yes.name())){
                    evOrder.setPlaceStatus(OrderEnum.PlaceStatus.Place.name());
                    evOrder.setPlaceBeginTime(new Date());
                    evOrderService.updateRow(evOrder);
                }
            }
        }
    }

    /**
     * 设备端口上报
     *
     * @param seq        序列号
     * @param imei IMEI码
     * @param realtimeData 实时数据
     */
    @Override
    public void reqRealtimeData(String seq, String imei,EVRealtimeData realtimeData){
        log.info("汽车充电桩[{}]端口上报：{}", imei, JSONUtil.toJsonStr(realtimeData));
        self.selfReqRealtimeData(seq, imei, realtimeData);
    }

    /**
     * 设备端口上报
     *
     * @param seq        序列号
     * @param imei IMEI码
     * @param realtimeData 实时数据
     */
    @Lock4j(name = "orderNotify", keys = {"#imei"})
    public void selfReqRealtimeData(String seq, String imei,EVRealtimeData realtimeData){
        log.info("汽车充电桩[{}]端口上报：{}", imei, JSONUtil.toJsonStr(realtimeData));

        //更新设备状态
        Pile pile = pileService.getRow("imei",imei);
        if(pile != null){
            Gun gun = gunService.getRow(new String[]{"device_pile_id","port"}, new Object[]{pile.getDevicePileId(),realtimeData.getGunNo()});
            if(gun != null){

                //获取枪正在充电的订单
                if(realtimeData.getGunInserted().equals(YesOrNo.No.name())){//0:未插枪
                    List<Order> orders = orderService.getViewModelList("get_ev_place_orders",Order.class,new Object[]{imei, realtimeData.getGunNo(), OrderEnum.PlaceStatus.Place.name()});
                    for(Order order : orders){
                        EvOrder evOrder = service.getRow("charge_order_id",order.getChargeOrderId());
                        //计算占位费:订单已完成且在占位中状态时，未插枪，才计算点位费
                        if(order.getStatus().equals(OrderEnum.Status.Completed.name()) &&
                                realtimeData.getGunInserted().equals(YesOrNo.No.name()) &&
                                evOrder.getPlaceStatus().equals(OrderEnum.PlaceStatus.Place.name())){
                            //结算占位费
                            finishPlaceOrder(evOrder);
                        }

                    }
                }

                //未插枪，清空该枪的订单
                if(StrUtil.isNotEmpty(gun.getOrderNo()) && realtimeData.getGunInserted().equals(YesOrNo.No.name())){
                    gun.setOrderNo(StrUtil.EMPTY);
                }
                gun.setStatus(realtimeData.getStatus());
                gun.setIsInserted(realtimeData.getGunInserted());
                gun.setSoc(realtimeData.getSoc());
                gun.setChargeTime(realtimeData.getTotalChargeMinutes());
                gun.setRemainTime(realtimeData.getRemainMinutes());
                gun.setCurrent(NumberUtil.div(realtimeData.getElectricity(), 10));
                gun.setVoltage(NumberUtil.div(realtimeData.getVoltage(), 10));
                gun.setTemp(Math.max(0,realtimeData.getGunLineTemperature() - 50));
                gun.setBatteryTemp(Math.max(0,realtimeData.getBatteryMaxTemperature() - 50));


                //设备故障
                if(DeviceEnum.PortStatus.Faulted.name().equals(realtimeData.getStatus())){
                    gun.setErrorCodes(String.valueOf(realtimeData.getHardwareError()));
                    try{
                        if(realtimeData.getHardwareError().size() > 0) {
                            alarmRecordService.add(pile.getDeviceStationId(), pile.getDevicePileId(), gun.getDeviceGunId(),
                                    CollUtil.join(realtimeData.getHardwareError(), "、"), null);
                        }
                    }catch (Exception e){
                        log.error("设备[{}]故障上报错误", imei, e);
                    }

                }else{
                    gun.setErrorCodes(String.valueOf(new ArrayList<>()));
                }

                //未插枪，清空该枪的订单
                if(StrUtil.isNotEmpty(gun.getOrderNo()) && realtimeData.getGunInserted().equals(YesOrNo.No.name())){
                    gun.setOrderNo(StrUtil.EMPTY);
                }
                gunService.updateRow(gun);
            }
        }

        // 更新充电中订单信息
        Order order = orderService.getRow("order_no",realtimeData.getTransactionNo());
        if(order != null){
            if(OrderEnum.Status.Charging.name().equals(order.getStatus())){
                updateChargingOrder(order, realtimeData);
            }else if(OrderEnum.Status.Completed.name().equals(order.getStatus()) && DeviceEnum.PortStatus.Charging.name().equals(realtimeData.getStatus())){//订单已结束，则下发给桩停充
                log.info("订单已结束，桩未结束：{},{},{}",order.getOrderNo(),order.getStatus(),realtimeData.getStatus());
                taskScheduler.schedule(() -> evOrderTask.pushDeviceStopCharge(pile.getProtocol(),imei, realtimeData.getGunNo()),
                        DateUtil.date().offset(DateField.SECOND, 3));
            }
        }
    }

    /**
     * 更新充电中订单信息
     * @param order
     * @param realtimeData
     */
    private void updateChargingOrder(Order order, EVRealtimeData realtimeData){

        // 兼容设备上报数据差异化问题
        if(realtimeData.getChargeCapacity() == 0 && realtimeData.getVoltage() == 0
                && realtimeData.getElectricity() == 0 && realtimeData.getTotalChargeMinutes() == 0){
            return;
        }

        EvOrder evOrder = evOrderService.getRow("charge_order_id",order.getChargeOrderId());
        if(null == evOrder){
            return;
        }

        evOrder.setChargeCurrent(NumberUtil.div(realtimeData.getElectricity(), 10));
        evOrder.setChargeVoltage(NumberUtil.div(realtimeData.getVoltage(), 10));
        evOrder.setChargeMinutes(realtimeData.getTotalChargeMinutes());
        evOrder.setRemainMinutes(realtimeData.getRemainMinutes());
        evOrder.setChargeCapacity(realtimeData.getChargeCapacity());
        evOrder.setChargePower(NumberUtil.mul(evOrder.getChargeCurrent(), evOrder.getChargeVoltage()));
        if(evOrder.getBeginSoc() == 0){
            evOrder.setBeginSoc(realtimeData.getSoc());
        }
        evOrder.setSoc(realtimeData.getSoc());
        evOrder.setGunTemperature(Math.max(0,realtimeData.getGunLineTemperature() - 50));
        evOrder.setBatteryTemperature(Math.max(0,realtimeData.getBatteryMaxTemperature() - 50));
        evOrder.setReportAmount(realtimeData.getChargeAmount());

        order.setChargeCapacity(realtimeData.getChargeCapacity());//多记录一个电量字段

        if(null != order.getStartTime()){
            order.setDuration((int) DateUtil.between(DateUtil.date(), order.getStartTime(), DateUnit.SECOND));
        }

        if(order.getStatus().equals(OrderEnum.Status.Charging.name())){
            try {
                this.saveOrderFee(order);
            } catch (Exception e) {
                log.error("更新订单费用失败 >>> \n 订单号：{} \n 错误原因：{}", order.getOrderNo(), e);
            }

            List<OrderFee> orderFeeList = orderFeeService.getRows("charge_order_id", order.getChargeOrderId());
            evOrder.setServiceAmount(orderFeeList.stream().mapToInt(item -> item.getServiceAmount()).sum());
            order.setChargeAmount(orderFeeList.stream().mapToInt(item -> item.getChargeAmount()).sum());
            order.setOrderAmount(realtimeData.getChargeAmount());
            if(YesOrNo.Yes.name().equals(order.getIsFree())){
                order.setFreeAmount(order.getOrderAmount());
                order.setChargeAmount(0);
                order.setOrderAmount(0);
            }
        }

        orderService.updateRow(order);
        evOrderService.updateRow(evOrder);

        // 记录充电功率
        OrderPower item = new OrderPower();
        item.setComId(order.getComId());
        item.setChargeOrderId(order.getChargeOrderId());
        item.setCreateTime(new Date());
        item.setEnergy(realtimeData.getChargeCapacity() * 1L);
        item.setPower(NumberUtil.mul(evOrder.getChargeCurrent(), evOrder.getChargeVoltage()));
        item.setStatus(realtimeData.getStatus());
        item.setCurrent(NumberUtil.div(realtimeData.getElectricity(), 10));
        item.setVoltage(NumberUtil.div(realtimeData.getVoltage(), 10));
        item.setTime(order.getDuration());
        orderPowerService.saveRow(item);

        saveChargeData(order.getComId(),order.getChargeOrderId(),OrderAction.EV.TimeAndCharge,item.getCreateTime(),
                realtimeData,null);

        // 充电完成且未插枪，结束占位
        if(order.getStatus().equals(OrderEnum.Status.Completed.name()) && realtimeData.getGunInserted().equals(YesOrNo.No.name())){
            finishPlaceOrder(evOrder);
        }
    }


    /**
     * 充电中错误上报
     *
     * @param seq        序列号
     * @param imei IMEI码
     * @param chargingErrorData  错误报文
     */
    @Override
    public void reqChargingError(String seq, String imei, EVChargingErrorData chargingErrorData){
        log.info("汽车充电桩[{}]充电中错误上报：{}", imei, JSONUtil.toJsonStr(chargingErrorData));

    }

    /**
     * 充电过程中，暂停充电
     *
     * @param seq        序列号
     * @param imei IMEI码
     * @param suspendData   暂停数据
     */
    @Override
    public void reqChargingSuspend(String seq, String imei, EVSuspendData suspendData){
        log.info("汽车充电桩[{}]充电中，暂停充电上报：{}", imei, JSONUtil.toJsonStr(suspendData));

    }

    /**
     * 充电过程中，上报充电桩信息
     *
     * @param seq        序列号
     * @param imei IMEI码
     * @param chargerData   充电桩信息
     */
    @Override
    public void reqChargingChargerInfo(String seq, String imei, EVChargingChargerData chargerData){
        log.info("汽车充电桩[{}]充电中，充电桩信息上报：{}", imei, JSONUtil.toJsonStr(chargerData));

        EvBmsPower entity = BeanUtil.toBean(chargerData, EvBmsPower.class);
        entity.setCreateTime(DateUtil.date());
        Order order = orderService.getRow(new String[]{"device_imei","port","status"},
                new Object[]{entity.getCode(),entity.getGunNo(),OrderEnum.Status.Charging.name()});
        if(null != order){
            entity.setChargeOrderId(order.getChargeOrderId());
            entity.setComId(order.getComId());
        }
        bmsPowerService.saveRow(entity);

    }

    /**
     * 充电过程中，上报电池信息
     *
     * @param seq        序列号
     * @param imei IMEI码
     * @param bmsData   电池信息
     */
    @Override
    public void reqChargingBmsInfo(String seq, String imei, EVChargingBmsData bmsData){
        log.info("汽车充电桩[{}]充电中，电池信息上报：{}", imei, JSONUtil.toJsonStr(bmsData));

        EvBmsInfo entity = BeanUtil.toBean(bmsData, EvBmsInfo.class);
        entity.setCreateTime(DateUtil.date());
        Order order = orderService.getRow(new String[]{"device_imei","port","status"},
                new Object[]{entity.getCode(),entity.getGunNo(),OrderEnum.Status.Charging.name()});
        if(null != order){
            entity.setChargeOrderId(order.getChargeOrderId());
            entity.setComId(order.getComId());
        }
        bmsInfoService.saveRow(entity);
    }

    /**
     * 宝付微信支付回调
     *
     *
     */
    public void baofuWxPayNotify(String outTradeNo, String transactionId,String payTime, String comId) {

//
//        String outTradeNo = wxPayOrderNotifyV3Result.getResult().getOutTradeNo();
//        DateTime payTime = DateUtil.parse(wxPayOrderNotifyV3Result.getResult().getSuccessTime());
//        String transactionId = wxPayOrderNotifyV3Result.getResult().getTransactionId();
        this.payFinish(outTradeNo, transactionId, DateUtil.parse(payTime), PayEnum.PayWay.Wxpay);
    }
}
